SPA ๋ฐ MPA ๋ชจ๋์ ๋ํด ์ฑ๊ณผ ๊ฐ์ ํ์ด์ง ํ์์ ์์ฑํ๊ธฐ ์ํด CSS ๋ทฐ ์ ํ API๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ๋ํ ๊ฐ๋ฐ์๋ฅผ ์ํ ํฌ๊ด์ ์ธ ๊ฐ์ด๋์ ๋๋ค. ํต์ฌ ๊ฐ๋ ๊ณผ ๊ณ ๊ธ ๊ธฐ์ ์ ์์๋ณด์ธ์.
CSS ๋ทฐ ์ ํ API: ๋ถ๋๋ฌ์ด ํ์ด์ง ํ์ ๊ตฌํ์ ์ํ ๊ถ๊ทน์ ์ธ ๊ฐ์ด๋
์์ญ ๋ ๋์ ์น ํ์์ ์ถฉ๊ฒฉ์ ์ธ ํ์ค, ์ฆ ๋น ํฐ์ ํ๋ฉด์ผ๋ก ์ ์๋์ด ์์ต๋๋ค. ๋งํฌ๋ฅผ ํด๋ฆญํ๋ ๊ฒ์ ์ ์ฒด ํ์ด์ง ์ฌ๋ก๋๋ฅผ ์๋ฏธํ์ผ๋ฉฐ, ์ ์ ๋์ ์๋ฌด๊ฒ๋ ๋ํ๋์ง ์๋ค๊ฐ ์ ์ฝํ ์ธ ๊ฐ ๊ฐ์๊ธฐ ๋ํ๋ฌ์ต๋๋ค. ๊ธฐ๋ฅ์ ์ด๊ธฐ๋ ํ์ง๋ง ์ด ๊ฒฝํ์ ๋ค์ดํฐ๋ธ ์ ํ๋ฆฌ์ผ์ด์ ์์ ๊ธฐ๋ํ ์ ์๋ ์ ๋์ฑ๊ณผ ์ธ๋ จ๋ฏธ๊ฐ ๋ถ์กฑํฉ๋๋ค. ์ฑ๊ธ ํ์ด์ง ์ ํ๋ฆฌ์ผ์ด์ (SPA)์ ์ํคํ ์ฒ์ ๋จ์์ฑ๊ณผ ์ด๊ธฐ ๋ก๋ ์ฑ๋ฅ์ ํฌ์ํ๋ฉด์ ๋ณต์กํ JavaScript ํ๋ ์์ํฌ๋ฅผ ์ฌ์ฉํ์ฌ ์ํํ ์ ํ์ ๋ง๋ค ์ ์๋ ์๋ฃจ์ ์ผ๋ก ๋ฑ์ฅํ์ต๋๋ค.
๋ ๊ฐ์ง ๋ชจ๋๋ฅผ ํ์ฉํ ์ ์๋ค๋ฉด ์ด๋จ๊น์? ๋ฉํฐ ํ์ด์ง ์ ํ๋ฆฌ์ผ์ด์ (MPA)์ ๋จ์ํ ์๋ฒ ๋ ๋๋ง ์ํคํ ์ฒ์ SPA์ ์ฐ์ํ๊ณ ์๋ฏธ ์๋ ์ ํ์ด ๊ฒฐํฉ๋ ๊ฒ์ ๋๋ค. ์ด๊ฒ์ด ๋ฐ๋ก ์น ๊ฐ๋ฐ ๋ฐ ์ฌ์ฉ์ ๊ฒฝํ ๊ตฌ์ถ ๋ฐฉ์์ ํ๋ช ์ ์ผ์ผํฌ CSS ๋ทฐ ์ ํ API์ ์ฝ์์ ๋๋ค.
์ด ํฌ๊ด์ ์ธ ๊ฐ์ด๋๋ ๋ทฐ ์ ํ API์ ๋ํ ์ฌ์ธต์ ์ธ ๋ถ์์ ์ ๊ณตํฉ๋๋ค. ๋ทฐ ์ ํ API๊ฐ ๋ฌด์์ธ์ง, ์น ๊ฐ๋ฐ์ ์์ด์ ํ๊ธฐ์ ์ธ ๋ณํ์ธ ์ด์ , ๊ทธ๋ฆฌ๊ณ ์ค๋๋ SPA์, ๋์ฑ ํฅ๋ฏธ๋กญ๊ฒ๋ ๊ธฐ์กด MPA์ ๋ํด ๋ทฐ ์ ํ API๋ฅผ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ ์ดํด๋ณด๊ฒ ์ต๋๋ค. ํฐ์ ํ๋์ ํ๋ฉด์ ์๋ณ์ ๊ณ ํ๊ณ ์ํํ ์น ํ์์ ์๋ก์ด ์๋๋ฅผ ๋ง์ดํ ์ค๋น๋ฅผ ํ์ธ์.
CSS ๋ทฐ ์ ํ API๋ ๋ฌด์์ธ๊ฐ์?
CSS ๋ทฐ ์ ํ API๋ ๊ฐ๋ฐ์๊ฐ ์๋ก ๋ค๋ฅธ DOM(Document Object Model) ์ํ ๊ฐ์ ์ ๋๋ฉ์ด์ ์ ํ์ ๋ง๋ค ์ ์๋๋ก ํ๋ ์น ํ๋ซํผ์ ์ง์ ๋ด์ฅ๋ ๋ฉ์ปค๋์ฆ์ ๋๋ค. ํต์ฌ์ ์ผ๋ก, ์ด API๋ ๋์ผํ ํ์ด์ง(SPA) ๋๋ ๋ ๊ฐ์ ๋ค๋ฅธ ๋ฌธ์(MPA) ๊ฐ์ ๋ฐ์ํ๋์ง ์ฌ๋ถ์ ๊ด๊ณ์์ด ํ ๋ณด๊ธฐ์์ ๋ค๋ฅธ ๋ณด๊ธฐ๋ก์ ์๊ฐ์ ๋ณํ๋ฅผ ๊ด๋ฆฌํ๋ ๊ฐ๋จํ ๋ฐฉ๋ฒ์ ์ ๊ณตํฉ๋๋ค.
ํ๋ก์ธ์ค๋ ๋งค์ฐ ์๋ฆฌํฉ๋๋ค. ์ ํ์ด ํธ๋ฆฌ๊ฑฐ๋๋ฉด ๋ธ๋ผ์ฐ์ ๋ ๋ค์์ ์ํํฉ๋๋ค.
- ํ์ฌ ํ์ด์ง ์ํ(์ด์ ๋ณด๊ธฐ)์ "์คํฌ๋ฆฐ์ท"์ ์ฐ์ต๋๋ค.
- ์ ์ํ๋ก DOM์ ์ ๋ฐ์ดํธํ ์ ์์ต๋๋ค.
- ์ ํ์ด์ง ์ํ(์ ๋ณด๊ธฐ)์ "์คํฌ๋ฆฐ์ท"์ ์ฐ์ต๋๋ค.
- ์ด์ ๋ณด๊ธฐ์ ์คํฌ๋ฆฐ์ท์ ์ ๋ผ์ด๋ธ ๋ณด๊ธฐ ์์ ๋ฐฐ์นํฉ๋๋ค.
- ๊ธฐ๋ณธ์ ์ผ๋ก ๋ถ๋๋ฌ์ด ํฌ๋ก์คํ์ด๋๋ก ๋ ๋ณด๊ธฐ ๊ฐ์ ์ ํ์ ์ ๋๋ฉ์ด์ ํํฉ๋๋ค.
์ด ์ ์ฒด ํ๋ก์ธ์ค๋ ๋ธ๋ผ์ฐ์ ์ ์ํด ์กฐ์ ๋๋ฏ๋ก ์ฑ๋ฅ์ด ๋งค์ฐ ๋ฐ์ด๋ฉ๋๋ค. ๋ ์ค์ํ ๊ฒ์ ๊ฐ๋ฐ์์๊ฒ ํ์ค CSS๋ฅผ ์ฌ์ฉํ์ฌ ์ ๋๋ฉ์ด์ ์ ์๋ฒฝํ๊ฒ ์ ์ดํ ์ ์๋๋ก ํ์ฌ, ํ๋ ๋ณต์กํ JavaScript ์์ ์ ์ ์ธ์ ์ด๊ณ ์ ๊ทผ ๊ฐ๋ฅํ ์คํ์ผ๋ง ๋ฌธ์ ๋ก ์ ํํ๋ค๋ ๊ฒ์ ๋๋ค.
์น ๊ฐ๋ฐ์ ํ๋๋ฅผ ๋ฐ๊พธ๋ ์ด์
์ด API์ ๋์ ์ ๋ ๋ค๋ฅธ ์ ์ง์ ์ธ ์ ๋ฐ์ดํธ๊ฐ ์๋๋ผ ์น ํ๋ซํผ์ ๊ทผ๋ณธ์ ์ธ ๊ฐ์ ์ ๋ํ๋ ๋๋ค. ์ด๋ ์ ์ธ๊ณ ๊ฐ๋ฐ์์ ์ฌ์ฉ์์๊ฒ ๋งค์ฐ ์ค์ํ ์ด์ ์ ๋๋ค.
- ๊ทน์ ์ผ๋ก ํฅ์๋ ์ฌ์ฉ์ ๊ฒฝํ(UX): ๋ถ๋๋ฌ์ด ์ ํ์ ๋จ์ํ ์ฅ์์ด ์๋๋๋ค. ์๋ก ๋ค๋ฅธ ๋ณด๊ธฐ ๊ฐ์ ๊ด๊ณ๋ฅผ ์ดํดํ๋ ๋ฐ ๋์์ด ๋๋ ์๊ฐ์ ์ฐ์์ฑ์ ์ ๊ณตํฉ๋๋ค. ์ธ๋ค์ผ์์ ์ ์ฒด ํฌ๊ธฐ ์ด๋ฏธ์ง๋ก ์ํํ๊ฒ ํ๋๋๋ ์์๋ ์ปจํ ์คํธ๋ฅผ ์ ๊ณตํ๊ณ ์ฌ์ฉ์์ ์ฃผ์๋ฅผ ์ง์ค์์ผ ์ธํฐํ์ด์ค๊ฐ ๋ ์ง๊ด์ ์ด๊ณ ๋ฐ์์ฑ์ด ๋ฐ์ด๋๋๋ก ํฉ๋๋ค.
- ๋ํญ ๊ฐ์ํ๋ ๊ฐ๋ฐ: ์ด API๊ฐ ๋์ค๊ธฐ ์ ์๋ ์ด์ ์ ์ฌํ ํจ๊ณผ๋ฅผ ์ป์ผ๋ ค๋ฉด ๋ฌด๊ฑฐ์ด JavaScript ๋ผ์ด๋ธ๋ฌ๋ฆฌ(์: Framer Motion ๋๋ GSAP) ๋๋ ๋ณต์กํ CSS-in-JS ์๋ฃจ์ ์ด ํ์ํ์ต๋๋ค. ๋ทฐ ์ ํ API๋ ์ด ๋ณต์ก์ฑ์ ๊ฐ๋จํ ํจ์ ํธ์ถ๊ณผ ๋ช ์ค์ CSS๋ก ๋์ฒดํ์ฌ ์๋ฆ๋ค์ด ์ฑ๊ณผ ๊ฐ์ ๊ฒฝํ์ ๋ง๋๋ ๋ฐ ํ์ํ ์ง์ ์ฅ๋ฒฝ์ ๋ฎ์ถฅ๋๋ค.
- ๋ฐ์ด๋ ์ฑ๋ฅ: ์ ๋๋ฉ์ด์ ๋ก์ง์ ๋ธ๋ผ์ฐ์ ์ ๋ ๋๋ง ์์ง์ ์คํ๋ก๋ํ๋ฉด ๋ทฐ ์ ํ์ด JavaScript ๊ธฐ๋ฐ ๋ฐฉ์๋ณด๋ค ์ฑ๋ฅ์ด ๋ฐ์ด๋๊ณ ๋ฐฐํฐ๋ฆฌ ํจ์จ์ฑ์ด ๋์์ง๋๋ค. ๋ธ๋ผ์ฐ์ ๋ ์๋์ผ๋ก ๋ณต์ ํ๊ธฐ ์ด๋ ค์ด ๋ฐฉ์์ผ๋ก ํ๋ก์ธ์ค๋ฅผ ์ต์ ํํ ์ ์์ต๋๋ค.
- SPA-MPA ๊ฒฉ์ฐจ ํด์: ์๋ง๋ ๊ฐ์ฅ ํฅ๋ฏธ๋ก์ด ์ธก๋ฉด์ ๊ต์ฐจ ๋ฌธ์ ์ ํ์ ๋ํ ์ง์์ผ ๊ฒ์ ๋๋ค. ์ด๋ฅผ ํตํด ๊ธฐ์กด์ ์๋ฒ ๋ ๋๋ง ์น์ฌ์ดํธ(MPA)๊ฐ ์ค๋ซ๋์ SPA์๋ง ๊ตญํ๋ ๊ฒ์ผ๋ก ์ฌ๊ฒจ์ก๋ ์ ๋์ ์ธ ํ์์ ์ฑํํ ์ ์์ต๋๋ค. ์ด์ ๊ธฐ์ ์ ์ ์ฒด SPA ํ๋ ์์ํฌ๋ก์ ๋ณต์กํ๊ณ ๋น์ฉ์ด ๋ง์ด ๋๋ ์ํคํ ์ฒ ๋ง์ด๊ทธ๋ ์ด์ ์ ์ํํ์ง ์๊ณ ๋ ๊ธฐ์กด ์น์ฌ์ดํธ๋ฅผ ์ต์ UX ํจํด์ผ๋ก ํฅ์์ํฌ ์ ์์ต๋๋ค.
ํต์ฌ ๊ฐ๋ : ๋ทฐ ์ ํ์ ๋ง๋ฒ ์ดํด
API๋ฅผ ๋ง์คํฐํ๋ ค๋ฉด ๋จผ์ ๋ ๊ฐ์ง ์ฃผ์ ๊ตฌ์ฑ ์์, ์ฆ JavaScript ํธ๋ฆฌ๊ฑฐ์ ์ฌ์ฉ์ ์ง์ ์ ๊ฐ๋ฅํ๊ฒ ํ๋ CSS ์์ฌ ์์ ํธ๋ฆฌ๋ฅผ ์ดํดํด์ผ ํฉ๋๋ค.
JavaScript ์ง์ ์ : `document.startViewTransition()`
๋ชจ๋ ๊ฒ์ ๋จ์ผ JavaScript ํจ์ `document.startViewTransition()`์ผ๋ก ์์ํฉ๋๋ค. ์ด ํจ์๋ ์ธ์๋ก ์ฝ๋ฐฑ์ ์ฌ์ฉํฉ๋๋ค. ์ด ์ฝ๋ฐฑ ๋ด์์ ์ด์ ์ํ์์ ์ ์ํ๋ก ์ ํํ๋ ๋ฐ ํ์ํ ๋ชจ๋ DOM ์กฐ์์ ์ํํฉ๋๋ค.
์ผ๋ฐ์ ์ธ ํธ์ถ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
// ๋จผ์ ๋ธ๋ผ์ฐ์ ๊ฐ API๋ฅผ ์ง์ํ๋์ง ํ์ธํฉ๋๋ค.
if (!document.startViewTransition) {
// ์ง์๋์ง ์๋ ๊ฒฝ์ฐ DOM์ ์ง์ ์
๋ฐ์ดํธํฉ๋๋ค.
updateTheDOM();
} else {
// ์ง์๋๋ ๊ฒฝ์ฐ ์ ํ ํจ์์ DOM ์
๋ฐ์ดํธ๋ฅผ ๋ํํฉ๋๋ค.
document.startViewTransition(() => {
updateTheDOM();
});
}
`startViewTransition`์ ํธ์ถํ๋ฉด ๋ธ๋ผ์ฐ์ ๊ฐ ์์์ ์ค๋ช ํ ์บก์ฒ-์ ๋ฐ์ดํธ-์ ๋๋ฉ์ด์ ์ํ์ค๋ฅผ ์์ํฉ๋๋ค. ์ด ํจ์๋ ๋ณด๋ค ๊ณ ๊ธ ์ ์ด๋ฅผ ์ํด ์ ํ ์๋ช ์ฃผ๊ธฐ์ ๋ค๋ฅธ ๋จ๊ณ์ ์ฐ๊ฒฐํ ์ ์๋ ํ๋ก๋ฏธ์ค๋ฅผ ํฌํจํ๋ `ViewTransition` ๊ฐ์ฒด๋ฅผ ๋ฐํํฉ๋๋ค.
CSS ์์ฌ ์์ ํธ๋ฆฌ
์ฌ์ฉ์ ์ง์ ์ ์ง์ ํ ํ์ ๋ธ๋ผ์ฐ์ ๊ฐ ์ ํ ์ค์ ์์ฑํ๋ ํน์ CSS ์์ฌ ์์ ์งํฉ์ ์์ต๋๋ค. ์ด ์์ ํธ๋ฆฌ๋ฅผ ํตํด ์ด์ ๋ณด๊ธฐ์ ์ ๋ณด๊ธฐ๋ฅผ ๋ ๋ฆฝ์ ์ผ๋ก ์คํ์ผ๋งํ ์ ์์ต๋๋ค.
::view-transition: ์ ์ฒด ๋ทฐํฌํธ๋ฅผ ๋ฎ๋ ํธ๋ฆฌ์ ๋ฃจํธ์ ๋๋ค. ์ด๋ฅผ ์ฌ์ฉํ์ฌ ์ ํ์ ๋ฐฐ๊ฒฝ์ ๋๋ ์ง์ ์๊ฐ์ ์ค์ ํ ์ ์์ต๋๋ค.::view-transition-group(name): ๋จ์ผ ์ ํ ์์๋ฅผ ๋ํ๋ ๋๋ค. ์ ๋๋ฉ์ด์ ์ค์ ์์์ ์์น์ ํฌ๊ธฐ๋ฅผ ๋ด๋นํฉ๋๋ค.::view-transition-image-pair(name): ์์์ ์ด์ ๋ณด๊ธฐ์ ์ ๋ณด๊ธฐ๋ฅผ ์ํ ์ปจํ ์ด๋์ ๋๋ค. ๊ฒฉ๋ฆฌ๋mix-blend-mode๋ก ์คํ์ผ๋ง๋ฉ๋๋ค.::view-transition-old(name): ์์์ ์ด์ ์ํ(์: ์ธ๋ค์ผ)์ ์คํฌ๋ฆฐ์ท์ ๋๋ค.::view-transition-new(name): ์์์ ์ ์ํ(์: ์ ์ฒด ํฌ๊ธฐ ์ด๋ฏธ์ง)์ ๋ผ์ด๋ธ ํํ์ ๋๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก ์ด ํธ๋ฆฌ์ ์ ์ผํ ์์๋ ์ ์ฒด ํ์ด์ง๋ฅผ ๋ํ๋ด๋ `root`์ ๋๋ค. ํน์ ์์ ๊ฐ์ ์ ๋๋ฉ์ด์ ์ ์ ์ฉํ๋ ค๋ฉด ์ผ๊ด๋ `view-transition-name`์ ์ง์ ํด์ผ ํฉ๋๋ค.
์ค์ฉ์ ์ธ ๊ตฌํ: ์ฒซ ๋ฒ์งธ ๋ทฐ ์ ํ(SPA ์)
์ผ๋ฐ์ ์ธ UI ํจํด์ ๊ตฌ์ถํด ๋ณด๊ฒ ์ต๋๋ค. ์ฆ, ํด๋ฆญํ๋ฉด ๋์ผํ ํ์ด์ง์์ ์์ธํ ๋ณด๊ธฐ๋ก ์ ํ๋๋ ์นด๋ ๋ชฉ๋ก์ ๋๋ค. ํต์ฌ์ ๋ ์ํ ๊ฐ์ ๋ถ๋๋ฝ๊ฒ ๋ณํ๋๋ ์ด๋ฏธ์ง์ ๊ฐ์ ๊ณต์ ์์๊ฐ ์๋ ๊ฒ์ ๋๋ค.
1๋จ๊ณ: HTML ๊ตฌ์กฐ
๋ชฉ๋ก์ ์ปจํ ์ด๋์ ์ธ๋ถ ๋ณด๊ธฐ์ ์ปจํ ์ด๋๊ฐ ํ์ํฉ๋๋ค. ์์ ์์์ ํด๋์ค๋ฅผ ํ ๊ธํ์ฌ ํ๋๋ฅผ ํ์ํ๊ณ ๋ค๋ฅธ ํ๋๋ฅผ ์จ๊น๋๋ค.
<div id="app-container">
<div class="list-view">
<!-- ์นด๋ 1 -->
<div class="card" data-id="item-1">
<img src="thumbnail-1.jpg" alt="Item 1">
<h3>Product One</h3>
</div>
<!-- ๋ ๋ง์ ์นด๋... -->
</div>
<div class="detail-view" hidden>
<img src="large-1.jpg" alt="Item 1">
<h1>Product One</h1>
<p>์ฌ๊ธฐ์ ์์ธํ ์ค๋ช
์ด ์์ต๋๋ค...</p>
<button id="back-button">Back</button>
</div>
</div>
2๋จ๊ณ: `view-transition-name` ํ ๋น
๋ธ๋ผ์ฐ์ ๊ฐ ์ธ๋ค์ผ ์ด๋ฏธ์ง์ ์ธ๋ถ ๋ณด๊ธฐ ์ด๋ฏธ์ง๊ฐ *๋์ผํ ๊ฐ๋ ์ ์์*์์ ์ดํดํ๋ ค๋ฉด CSS์์ ๋์ผํ `view-transition-name`์ ์ง์ ํด์ผ ํฉ๋๋ค. ์ด ์ด๋ฆ์ ์ฃผ์ด์ง ์๊ฐ์ ํ์ด์ง์ ๊ฐ ์ ํ ์์์ ๋ํด ๊ณ ์ ํด์ผ ํฉ๋๋ค.
.card.active img {
view-transition-name: product-image;
}
.detail-view.active img {
view-transition-name: product-image;
}
JavaScript๋ฅผ ์ฌ์ฉํ์ฌ ์ถ๊ฐํ `.active` ํด๋์ค๋ฅผ ์ฌ์ฉํ์ฌ ๋ณด์ด๋ ์์์๋ง ์ด๋ฆ์ ํ ๋นํ๊ณ ์ถฉ๋์ ๋ฐฉ์งํฉ๋๋ค.
3๋จ๊ณ: JavaScript ๋ ผ๋ฆฌ
์ด์ DOM ์ ๋ฐ์ดํธ๋ฅผ ์ฒ๋ฆฌํ๊ณ `document.startViewTransition()`์ผ๋ก ๋ํํ๋ ํจ์๋ฅผ ์์ฑํฉ๋๋ค.
function showDetailView(itemId) {
const updateDOM = () => {
// ์ฌ๋ฐ๋ฅธ ์นด๋์ ์ธ๋ถ ๋ณด๊ธฐ์ 'active' ํด๋์ค๋ฅผ ์ถ๊ฐํฉ๋๋ค.
// ์ด๊ฒ์ ๋ํ CSS๋ฅผ ํตํด view-transition-name์ ํ ๋นํฉ๋๋ค.
document.querySelector(`.card[data-id='${itemId}']`).classList.add('active');
document.querySelector('.detail-view').classList.add('active');
// ๋ชฉ๋ก์ ์จ๊ธฐ๊ณ ์ธ๋ถ ๋ณด๊ธฐ๋ฅผ ํ์ํฉ๋๋ค.
document.querySelector('.list-view').hidden = true;
document.querySelector('.detail-view').hidden = false;
};
if (!document.startViewTransition) {
updateDOM();
return;
}
document.startViewTransition(() => updateDOM());
}
์ด๊ฒ๋ง์ผ๋ก ์นด๋๋ฅผ ํด๋ฆญํ๋ฉด ์ด๋ฏธ์ง์ ๋ถ๋๋ฌ์ด ๋ณํ ์ ๋๋ฉ์ด์ ์ด ํธ๋ฆฌ๊ฑฐ๋๊ณ ํ์ด์ง์ ๋๋จธ์ง ๋ถ๋ถ์ ํฌ๋ก์คํ์ด๋๊ฐ ๋ํ๋ฉ๋๋ค. ๋ณต์กํ ์ ๋๋ฉ์ด์ ํ์๋ผ์ธ์ด๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ํ์ํ์ง ์์ต๋๋ค.
๋ค์ ํ๋ก ํฐ์ด: MPA๋ฅผ ์ํ ๊ต์ฐจ ๋ฌธ์ ๋ทฐ ์ ํ
์ด API๊ฐ ์ง์ ์ผ๋ก ํ์ ์ ์ธ ๊ณณ์ ๋๋ค. ์ด๋ฌํ ๋ถ๋๋ฌ์ด ์ ํ์ ๊ธฐ์กด์ ๋ฉํฐ ํ์ด์ง ์ ํ๋ฆฌ์ผ์ด์ (MPA)์ ์ ์ฉํ๋ ๊ฒ์ ์ด์ ์ MPA๋ฅผ SPA๋ก ์ ํํ์ง ์๊ณ ๋ ๋ถ๊ฐ๋ฅํ์ต๋๋ค. ์ด์ ๊ฐ๋จํ ์ตํธ์ธ ๋ฐฉ์์ ๋๋ค.
๊ต์ฐจ ๋ฌธ์ ์ ํ ํ์ฑํ
์๋ก ๋ค๋ฅธ ํ์ด์ง ๊ฐ์ ํ์์ ๋ํ ์ ํ์ ํ์ฑํํ๋ ค๋ฉด ์์ค ๋ฐ ๋์ ํ์ด์ง ๋ชจ๋์ CSS์ ๊ฐ๋จํ CSS at-rule์ ์ถ๊ฐํฉ๋๋ค.
@view-transition {
navigation: auto;
}
๊ทธ๊ฒ ์ ๋ถ์ ๋๋ค. ์ด ๊ท์น์ด ์์ผ๋ฉด ๋ธ๋ผ์ฐ์ ๋ ๋ชจ๋ ๋์ผ ์ถ์ฒ ํ์์ ๋ํด ์๋์ผ๋ก ๋ทฐ ์ ํ(๊ธฐ๋ณธ ํฌ๋ก์คํ์ด๋)์ ์ฌ์ฉํฉ๋๋ค.
ํต์ฌ: ์ผ๊ด๋ `view-transition-name`
SPA ์์ ์ ๋ง์ฐฌ๊ฐ์ง๋ก ๋ ๊ฐ์ ๋ณ๋ ํ์ด์ง์์ ์์๋ฅผ ์ฐ๊ฒฐํ๋ ๋ง๋ฒ์ ๊ณต์ ๋๊ณ ๊ณ ์ ํ `view-transition-name`์ ์์กดํฉ๋๋ค. ์ ํ ๋ชฉ๋ก ํ์ด์ง(`/products`)์ ์ ํ ์ธ๋ถ ์ ๋ณด ํ์ด์ง(`/products/item-1`)๋ฅผ ์์ํด ๋ณด๊ฒ ์ต๋๋ค.
`products.html`์์:
<a href="/products/item-1">
<img src="thumbnail-1.jpg" style="view-transition-name: product-image-1;">
</a>
`product-detail.html`์์:
<div class="hero">
<img src="large-1.jpg" style="view-transition-name: product-image-1;">
</div>
์ฌ์ฉ์๊ฐ ์ฒซ ๋ฒ์งธ ํ์ด์ง์์ ๋งํฌ๋ฅผ ํด๋ฆญํ๋ฉด ๋ธ๋ผ์ฐ์ ๋ `view-transition-name: product-image-1`์ด ์๋ ์์๊ฐ ํ์ด์ง๋ฅผ ๋ ๋๋ ๊ฒ์ ํ์ธํฉ๋๋ค. ๊ทธ๋ฐ ๋ค์ ์ ํ์ด์ง๊ฐ ๋ก๋๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฝ๋๋ค. ๋ ๋ฒ์งธ ํ์ด์ง๊ฐ ๋ ๋๋ง๋๋ฉด ๋์ผํ `view-transition-name`์ด ์๋ ์์๋ฅผ ์ฐพ๊ณ ๋ ์์ ๊ฐ์ ๋ถ๋๋ฌ์ด ๋ณํ ์ ๋๋ฉ์ด์ ์ ์๋์ผ๋ก ์์ฑํฉ๋๋ค. ๋๋จธ์ง ํ์ด์ง ์ฝํ ์ธ ๋ ๋ฏธ๋ฌํ ํฌ๋ก์คํ์ด๋๋ก ๊ธฐ๋ณธ ์ค์ ๋ฉ๋๋ค. ์ด๋ ์ด์ ์ MPA์์๋ ์์ํ ์ ์์๋ ์๋์ ์ฐ์์ฑ์ ๋ํ ์ธ์์ ์์ฑํฉ๋๋ค.
๊ณ ๊ธ ๊ธฐ์ ๋ฐ ์ฌ์ฉ์ ์ง์
๊ธฐ๋ณธ ํฌ๋ก์คํ์ด๋๋ ์ฐ์ํ์ง๋ง API๋ CSS ์ ๋๋ฉ์ด์ ์ ํตํด ์ฌ์ธต์ ์ธ ์ฌ์ฉ์ ์ง์ ํ ์ ์ ๊ณตํฉ๋๋ค.
CSS๋ฅผ ์ฌ์ฉํ ์ ๋๋ฉ์ด์ ์ฌ์ฉ์ ์ง์
ํ์ค CSS `@keyframes` ๋ฐ `animation` ์์ฑ์ ์ฌ์ฉํ์ฌ ์์ฌ ์์๋ฅผ ํ๊ฒํ ํ์ฌ ๊ธฐ๋ณธ ์ ๋๋ฉ์ด์ ์ ์ฌ์ ์ํ ์ ์์ต๋๋ค.
์๋ฅผ ๋ค์ด, ์ ํ์ด์ง ์ฝํ ์ธ ์ ๋ํด "์ค๋ฅธ์ชฝ์์ ์ฌ๋ผ์ด๋ ์ธ" ํจ๊ณผ๋ฅผ ๋ง๋ค๋ ค๋ฉด:
@keyframes slide-from-right {
from { transform: translateX(100%); }
}
::view-transition-new(root) {
animation: slide-from-right 0.5s ease-out;
}
`::view-transition-old` ๋ฐ `::view-transition-new`์ ์๋ก ๋ค๋ฅธ ์ ๋๋ฉ์ด์ ์ ์ ์ฉํ์ฌ ๊ณ ๋๋ก ์๋ฌด๋๊ณ ์ ๊ตํ ์ ํ์ ๋ง๋ค ์ ์์ต๋๋ค.
ํด๋์ค๋ฅผ ์ฌ์ฉํ ์ ํ ์ ํ ์ ์ด
์ผ๋ฐ์ ์ธ ์๊ตฌ ์ฌํญ์ ์๋ฐฉํฅ ๋ฐ ์ญ๋ฐฉํฅ ํ์์ ์๋ก ๋ค๋ฅธ ์ ๋๋ฉ์ด์ ์ ๊ฐ๋ ๊ฒ์ ๋๋ค. ์๋ฅผ ๋ค์ด ์๋ฐฉํฅ ํ์์ ์ ํ์ด์ง๋ฅผ ์ค๋ฅธ์ชฝ์์ ์ฌ๋ผ์ด๋ ์ธํ๋ ๋ฐ๋ฉด, ๋ค๋ก ํ์์ ์ผ์ชฝ์์ ์ฌ๋ผ์ด๋ ์ธํ ์ ์์ต๋๋ค. ์ด ์์ ์ ์ ํ์ ํธ๋ฆฌ๊ฑฐํ๊ธฐ ๋ฐ๋ก ์ ์ ๋ฌธ์ ์์(`<html>`)์ ํด๋์ค๋ฅผ ์ถ๊ฐํ์ฌ ์ํํ ์ ์์ต๋๋ค.
'๋ค๋ก' ๋ฒํผ์ ๋ํ JavaScript:
backButton.addEventListener('click', (event) => {
event.preventDefault();
document.documentElement.classList.add('is-going-back');
document.startViewTransition(() => {
// ๋ค๋ก ํ์ํ๋ ๋ก์ง
Promise.resolve().then(() => {
document.documentElement.classList.remove('is-going-back');
});
});
});
์๋ก ๋ค๋ฅธ ์ ๋๋ฉ์ด์ ์ ์ ์ํ๋ CSS:
/* ๊ธฐ๋ณธ ์๋ฐฉํฅ ์ ๋๋ฉ์ด์
*/
::view-transition-new(root) {
animation: slide-from-right 0.5s;
}
/* ๋ค๋ก ์ ๋๋ฉ์ด์
*/
.is-going-back::view-transition-new(root) {
animation: slide-from-left 0.5s;
}
์ด ๊ฐ๋ ฅํ ํจํด์ ์ฌ์ฉ์์ ํ์ ํ๊ฒฝ์ ์ธ๋ถ์ ์ผ๋ก ์ ์ดํ ์ ์๋๋ก ํฉ๋๋ค.
์ ๊ทผ์ฑ ๊ณ ๋ ค ์ฌํญ
ํ๋ ์น API๋ ๊ฐ๋ ฅํ ์ ๊ทผ์ฑ์ด ๋ด์ฅ๋์ด ์์ง ์์ผ๋ฉด ๋ถ์์ ํ๋ฉฐ, ๋ทฐ ์ ํ API๊ฐ ์ ๊ณตํฉ๋๋ค.
- ์ฌ์ฉ์ ๊ธฐ๋ณธ ์ค์ ์กด์ค: API๋ `prefers-reduced-motion` ๋ฏธ๋์ด ์ฟผ๋ฆฌ๋ฅผ ์๋์ผ๋ก ์กด์คํฉ๋๋ค. ์ฌ์ฉ์๊ฐ ์ด์ ์ฒด์ ์ค์ ์์ ๋ชจ์ ์ ์ค์ด๋ ๊ฒ์ ์ ํธํ๋ค๊ณ ํ์ํ ๊ฒฝ์ฐ, ์ ํ์ด ๊ฑด๋๋ฐ๊ณ DOM ์ ๋ฐ์ดํธ๊ฐ ์ฆ์ ๋ฐ์ํฉ๋๋ค. ์ด๊ฒ์ ๊ฐ๋ฐ์๋ก๋ถํฐ ์ถ๊ฐ ์์ ์์ด ๊ธฐ๋ณธ์ ์ผ๋ก ๋ฐ์ํฉ๋๋ค.
- ํฌ์ปค์ค ์ ์ง: ์ ํ์ ์์ ํ ์๊ฐ์ ์ ๋๋ค. ํ์ค ํฌ์ปค์ค ๊ด๋ฆฌ๋ฅผ ๋ฐฉํดํ์ง ์์ต๋๋ค. ์ ํ ํ ํค๋ณด๋ ํฌ์ปค์ค๋ฅผ ๊ธฐ๋ณธ ์ ๋ชฉ ๋๋ ์ฒซ ๋ฒ์งธ ๋ํํ ์์์ ๊ฐ์ด ์ ๋ณด๊ธฐ์ ๋ ผ๋ฆฌ์ ์์๋ก ์ด๋ํ๋๋ก ํ๋ ๊ฒ์ ๊ฐ๋ฐ์์ ์ฑ ์์ ๋๋ค.
- ์๋ฏธ๋ก ์ HTML: API๋ ๊ฐ์ ๋ ์ด์ด์ ๋๋ค. ๊ธฐ๋ณธ HTML์ ์๋ฏธ๋ก ์ ์ด๊ณ ์ ๊ทผ ๊ฐ๋ฅํ ์ํ๋ก ์ ์ง๋์ด์ผ ํฉ๋๋ค. ํ๋ฉด ํ๋ ๊ธฐ ๋๋ ์ง์ํ์ง ์๋ ๋ธ๋ผ์ฐ์ ๋ฅผ ์ฌ์ฉํ๋ ์ฌ์ฉ์๋ ์ ํ ์์ด ์ฝํ ์ธ ๋ฅผ ๊ฒฝํํ๋ฏ๋ก ๊ตฌ์กฐ๊ฐ ์์ฒด์ ์ผ๋ก ์๋ฏธ๊ฐ ์์ด์ผ ํฉ๋๋ค.
๋ธ๋ผ์ฐ์ ์ง์ ๋ฐ ์ ์ง์ ํฅ์
2023๋ ๋ง ํ์ฌ, SPA์ฉ ๋ทฐ ์ ํ API๋ Chromium ๊ธฐ๋ฐ ๋ธ๋ผ์ฐ์ (Chrome, Edge, Opera)์์ ์ง์๋ฉ๋๋ค. MPA์ ๋ํ ๊ต์ฐจ ๋ฌธ์ ์ ํ์ ๊ธฐ๋ฅ ํ๋๊ทธ ๋ค์์ ์ฌ์ฉํ ์ ์์ผ๋ฉฐ ์ ๊ทน์ ์ผ๋ก ๊ฐ๋ฐ ์ค์ ๋๋ค.
API๋ ์ฒ์๋ถํฐ ์ ์ง์ ํฅ์์ ์ํด ์ค๊ณ๋์์ต๋๋ค. ์์์ ์ฌ์ฉํ ๊ฐ๋ ํจํด์ด ํต์ฌ์ ๋๋ค.
if (!document.startViewTransition) { ... }
์ด ๊ฐ๋จํ ํ์ธ์ ์ฝ๋๊ฐ ์ด๋ฅผ ์ง์ํ๋ ๋ธ๋ผ์ฐ์ ์์๋ง ์ ํ์ ์๋ํ๋๋ก ํฉ๋๋ค. ์ด์ ๋ธ๋ผ์ฐ์ ์์๋ DOM ์ ๋ฐ์ดํธ๊ฐ ํญ์ ์ฆ์ ๋ฐ์ํฉ๋๋ค. ์ฆ, ์ค๋๋ถํฐ API๋ฅผ ์ฌ์ฉํ์ฌ ์ต์ ๋ธ๋ผ์ฐ์ ์ ์ฌ์ฉ์ ๊ฒฝํ์ ํฅ์์ํฌ ์ ์์ผ๋ฉฐ, ์ด์ ๋ธ๋ผ์ฐ์ ๋ฅผ ์ฌ์ฉํ๋ ์ฌ์ฉ์์๊ฒ๋ ๋ถ์ ์ ์ธ ์ํฅ์ด ์ ํ ์์ต๋๋ค. ์-์ ์๋๋ฆฌ์ค์ ๋๋ค.
์น ํ์์ ๋ฏธ๋
๋ทฐ ์ ํ API๋ ๋๊ธธ์ ๋๋ ์ ๋๋ฉ์ด์ ์ ์ํ ๋๊ตฌ ๊ทธ ์ด์์ ๋๋ค. ๊ฐ๋ฐ์๊ฐ ๋ ์ง๊ด์ ์ด๊ณ ์์ง๋ ฅ ์๊ณ ๋งค๋ ฅ์ ์ธ ์ฌ์ฉ์ ์ฌ์ ์ ๋ง๋ค ์ ์๋๋ก ์ง์ํ๋ ๊ทผ๋ณธ์ ์ธ ๋ณํ์ ๋๋ค. ์น ํ๋ซํผ์ ํ์ด์ง ์ ํ์ ํ์คํํ์ฌ ๋ค์ดํฐ๋ธ ์ ํ๋ฆฌ์ผ์ด์ ๊ณผ์ ๊ฒฉ์ฐจ๋ฅผ ์ขํ ๋ชจ๋ ์ ํ์ ์น์ฌ์ดํธ์์ ์๋ก์ด ์์ค์ ํ์ง๊ณผ ์ธ๋ จ๋ฏธ๋ฅผ ์ ๊ณตํฉ๋๋ค.
๋ธ๋ผ์ฐ์ ์ง์์ด ํ์ฅ๋จ์ ๋ฐ๋ผ ํ์ด์ง ๊ฐ์ ์ฌ์ ์ด ํ์ด์ง ์์ฒด๋งํผ ์ ์คํ๊ฒ ์ค๊ณ๋๋ ์น ๋์์ธ์์ ์๋ก์ด ์ฐฝ์์ฑ์ด ๋ํ๋ ๊ฒ์ผ๋ก ์์ํ ์ ์์ต๋๋ค. SPA์ MPA ๊ฐ์ ๊ฒฝ๊ณ๊ฐ ๊ณ์ ํ๋ ค์ง๋ฉด์ ํ์ ์ฌ์ฉ์ ๊ฒฝํ์ ํฌ์ํ์ง ์๊ณ ๋ ํ๋ก์ ํธ์ ๊ฐ์ฅ ์ ํฉํ ์ํคํ ์ฒ๋ฅผ ์ ํํ ์ ์์ต๋๋ค.
๊ฒฐ๋ก : ์ค๋๋ถํฐ ๋ ๋ถ๋๋ฌ์ด ๊ฒฝํ์ ๊ตฌ์ถํ์ธ์
CSS ๋ทฐ ์ ํ API๋ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ๊ณผ ๋๋ผ์ด ๋จ์ํจ์ ๋๋ฌธ ์กฐํฉ์ ์ ๊ณตํฉ๋๋ค. ์ด๋ ์ฌ์ดํธ์ ์ฌ์ฉ์ ๊ฒฝํ์ ๊ธฐ๋ฅ์ ์ธ ๊ฒ์์ ์ฆ๊ฑฐ์ด ๊ฒ์ผ๋ก ๋์ด์ฌ๋ฆด ์ ์๋, ์ฑ๋ฅ์ด ๋ฐ์ด๋๊ณ , ์ ๊ทผ ๊ฐ๋ฅํ๋ฉฐ, ์ ์ง์ ์ผ๋ก ํฅ์๋ ๋ฐฉ์์ ์ ๊ณตํฉ๋๋ค.
๋ณต์กํ SPA๋ฅผ ๊ตฌ์ถํ๋ ๊ธฐ์กด ์๋ฒ ๋ ๋๋ง ์น์ฌ์ดํธ๋ฅผ ๊ตฌ์ถํ๋ , ์ด์ ๋ ๋์ ๋๋ ํ์ด์ง ๋ก๋๋ฅผ ์ ๊ฑฐํ๊ณ ๋ถ๋๋ฝ๊ณ ์๋ฏธ ์๋ ์ ๋๋ฉ์ด์ ์ผ๋ก ์ธํฐํ์ด์ค๋ฅผ ํตํด ์ฌ์ฉ์๋ฅผ ์๋ดํ ์ ์๋ ๋๊ตฌ๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค. ๊ทธ ์ฑ๋ฅ์ ์ดํดํ๋ ๊ฐ์ฅ ์ข์ ๋ฐฉ๋ฒ์ ์๋ํด ๋ณด๋ ๊ฒ์ ๋๋ค. ์ ํ๋ฆฌ์ผ์ด์ ์ ์์ ๋ถ๋ถ(๊ฐค๋ฌ๋ฆฌ, ์ค์ ํ์ด์ง ๋๋ ์ ํ ํ๋ฆ)์ ๊ฐ์ ธ์ ์คํํด ๋ณด์ธ์. ๋ช ์ค์ ์ฝ๋๊ฐ ์น์ฌ์ดํธ์ ๋๋์ ๊ทผ๋ณธ์ ์ผ๋ก ๋ฐ๊ฟ ์ ์๋ค๋ ์ฌ์ค์ ๋๋ ๊ฒ์ ๋๋ค.
ํฐ์ ํ๋์ ์๋๋ ๋๋๊ฐ๊ณ ์์ต๋๋ค. ์น ํ์์ ๋ฏธ๋๋ ์ํํ๋ฉฐ ๋ทฐ ์ ํ API๋ฅผ ์ฌ์ฉํ๋ฉด ์ค๋๋ถํฐ ์น ํ์์ ๊ตฌ์ถํ๋ ๋ฐ ํ์ํ ๋ชจ๋ ๊ฒ์ ๊ฐ์ถ๊ฒ ๋ฉ๋๋ค.